无边框窗口如何实现拖拽、最大化和窗口控制?
考察点
- 无边框窗口的实现方式与平台差异
- 拖拽区域、双击最大化等细节处理
参考答案
1. 创建无边框窗口
new BrowserWindow({
frame: false, // 去掉系统边框(Windows/Linux)
titleBarStyle: 'hiddenInset', // macOS:保留红绿灯按钮但隐藏标题栏
// macOS 还可用 trafficLightPosition 微调红绿灯位置
});
平台策略:
- macOS:推荐
titleBarStyle: 'hidden' / 'hiddenInset',保留原生红绿灯(关闭/最小化/最大化),体验最原生 - Windows/Linux:
frame: false,三个控制按钮全部自己用 HTML 画
2. 拖拽区域
/* 标题栏区域可拖拽 */
.titlebar {
-webkit-app-region: drag;
}
/* 标题栏上的按钮/输入框不可拖拽,否则点不到 */
.titlebar button,
.titlebar input {
-webkit-app-region: no-drag;
}
3. 自己实现窗口控制按钮(Windows)
// 渲染进程通过桥接 API 调用
document.getElementById('min').onclick = () => window.api.window.min();
document.getElementById('max').onclick = () => window.api.window.toggleMax();
document.getElementById('close').onclick = () => window.api.window.close();
// 主进程实现
ipcMain.handle('window:toggle-max', (e) => {
const win = BrowserWindow.fromWebContents(e.sender);
win.isMaximized() ? win.unmaximize() : win.maximize();
});
同时要监听窗口状态同步按钮图标(最大化/还原切换):
win.on('maximize', () => win.webContents.send('window:max-changed', true));
win.on('unmaximize', () => win.webContents.send('window:max-changed', false));
4. 双击标题栏切换最大化
app-region 的 drag 区域在 macOS 上双击自动跟随系统设置(最大化或最小化),Windows 上需要自己处理:
document.querySelector('.titlebar').addEventListener('dblclick', () => {
window.api.window.toggleMax(); // 仅 Windows 需要
});
5. 常见坑
- Windows 最大化时遮挡任务栏/边框溢出:自绘边框在最大化时会超出屏幕几个像素,需在最大化态去掉外层 padding/border-radius。
- Windows Snap(拖标题栏贴边分屏):app-region drag 天然支持,但如果自己用 mouse 事件实现拖拽(不推荐)会丢失该能力。
- 可拖拽区域与滚动条冲突:drag 区域内的内容无法选中文本,注意交互边界。
win.isMaximized()在 Linux 部分桌面环境不可靠,需要降级方案。
面试话术
macOS 用 hiddenInset 保留红绿灯最省事;Windows 全自绘,核心点是 drag/no-drag 的区域划分、三按钮走 IPC 调主进程 API、状态变化要回推渲染进程同步图标,另外要处理最大化时的边框溢出。
常见追问
- 能不能拖拽时实时改窗口位置? 可以用 mouse 事件 +
win.setPosition,但会丢失系统原生拖拽体验(如 Windows Snap、macOS 空间切换),一般不推荐,除非要做异形窗口。 - 圆角窗口怎么做?
transparent: true+ CSS border-radius,注意透明窗口在 Windows 上不能阴影,需hasShadow或自绘。